package com.syh.jn.scm.domain.supplier.entity.stock;

import cn.redscarf.commons.spring.SpringContextUtils;
import cn.redscarf.commons.wrapper.Restrictions;
import cn.redscarf.commons.wrapper.Wrapper;
import com.syh.core.ddd.domain.AbstractSimpleAggregate;
import com.syh.core.ddd.domain.CodeExpEnum;
import com.syh.core.ddd.domain.CodeGenerateUtil;
import com.syh.jn.scm.domain.operation.entity.supplier.OperaSupplier;
import com.syh.jn.scm.domain.operation.repository.supplier.OperaSupplierReadRepository;
import com.syh.jn.scm.domain.supplier.entity.logistics.LogisticsBill;
import com.syh.jn.scm.domain.supplier.entity.logistics.LogisticsBillItem;
import com.syh.jn.scm.domain.supplier.repository.finance.archive.ArchiveReadRepository;
import com.syh.jn.scm.exception.BusinessException;
import com.syh.jn.scm.service.api.supplier.stock.enums.StockGoodsRecordEnums;
import lombok.Data;
import lombok.EqualsAndHashCode;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * 出入库记录
 */
@Data
@EqualsAndHashCode(callSuper = true)
public class StockRecord extends AbstractSimpleAggregate<Long> {

  public StockRecord() {
    this.initDefault();
  }

  /**
   * 创建物流单据时生成出入库记录
   * @param bill
   * @return
   */
  public void delivery(LogisticsBill bill) {
    this.businessEntityId = bill.getBusinessEntityId();
    this.checkSupplierOpenAccountState();
    this.checkArchiveState(bill.getDeliveryTimestamp());
    this.recordTimeStamp = bill.getDeliveryTimestamp();
    Short type;
    switch (bill.getOrderType()) {
      case 0://销售
        type = StockGoodsRecordEnums.Type.SALE_ORDER.getStatus();
        break;
      case 3://采购退货
        type = StockGoodsRecordEnums.Type.PURCHASE_RETURN_ORDER.getStatus();
        break;
      default:
        throw new BusinessException("订单类型异常！");
    }
    this.type = type;
    this.dataType = StockGoodsRecordEnums.DataType.NORMAL.getStatus();
    this.storehouseId = bill.getDepotId();
    this.originalOrderId = bill.getOrderId();
    this.originalOrderCode = bill.getOrderCode();
    this.originalLogisticsId = bill.getId();
    this.originalLogisticsCode = bill.getCode();
    this.recordOrderCode = CodeGenerateUtil.codeGenerate(CodeExpEnum.STOCK_RECORD_BILL_EXP.getCodeExp(), this.storehouseId);
    this.inOutType = StockGoodsRecordEnums.InOutType.OUT.getStatus();

    List<LogisticsBillItem> billItems = bill.getItems();
    this.items = new ArrayList<>();
    billItems.stream().forEach(billItem -> {
      StockRecordItem stockRecordItem = new StockRecordItem(bill,billItem,StockGoodsRecordEnums.DataType.NORMAL.getStatus());
      this.items.add(stockRecordItem);
    });
  }

  public void collect(LogisticsBill bill) {
    this.businessEntityId = bill.getBusinessEntityId();
    this.checkSupplierOpenAccountState();
    this.checkArchiveState(bill.getStorageTimestamp());
    this.recordTimeStamp = bill.getStorageTimestamp();
    Short type;
    switch (bill.getOrderType()) {
      case 1://销售退货
        type = StockGoodsRecordEnums.Type.SALE_RETURN_ORDER.getStatus();
        break;
      case 2://采购
        type = StockGoodsRecordEnums.Type.PURCHASE_ORDER.getStatus();
        break;
      default:
        throw new BusinessException("订单类型异常！");
    }

    this.type = type;
    this.dataType = StockGoodsRecordEnums.DataType.NORMAL.getStatus();
    this.storehouseId = bill.getDepotId();
    this.originalOrderId = bill.getOrderId();
    this.originalOrderCode = bill.getOrderCode();
    this.originalLogisticsId = bill.getId();
    this.originalLogisticsCode = bill.getCode();
    this.recordOrderCode = CodeGenerateUtil.codeGenerate(CodeExpEnum.STOCK_RECORD_BILL_EXP.getCodeExp(), this.storehouseId);
    this.inOutType = StockGoodsRecordEnums.InOutType.IN.getStatus();

    List<StockRecordItem> items = new ArrayList<>();
    List<LogisticsBillItem> billItems = bill.getItems();
    billItems.stream().forEach(billItem -> {
      StockRecordItem stockRecordItem = new StockRecordItem(bill,billItem,StockGoodsRecordEnums.DataType.NORMAL.getStatus());
      items.add(stockRecordItem);
    });
    this.items = items;
  }

  /**
   * 物流单修改时同时修改库存记录 TODO 物流单据修改商品数量需要未发车状态才能修改
   *
   * @param bill
   * @return
   */
//  public static StockRecord update(LogisticsBill bill) {
//    StockRecordRepository recordRepository = (StockRecordRepository) SpringContextUtils.getBean(StockRecordRepository.class);
//    StockRecord stockRecord = recordRepository.getOne(Wrapper.build().append(Restrictions.eq("originalLogisticsId",bill.getId())));
//
//    stockRecord.setStorehouseId(bill.getDepotId());
//    stockRecord.setLastUpdateTimeStamp(System.currentTimeMillis());
//    List<StockRecordItem> items = new ArrayList<>();
//    List<LogisticsBillItem> billItems = bill.getItems();
//    billItems.stream().forEach(billItem -> {
//      StockRecordItem stockRecordItem = new StockRecordItem(billItem);
//      items.add(stockRecordItem);
//    });
//    stockRecord.setItems(items);
//    return stockRecord;
//  }
  public void cancel(LogisticsBill bill) {
    this.businessEntityId = bill.getBusinessEntityId();
    this.checkSupplierOpenAccountState();
    this.checkArchiveState(bill.getDeliveryTimestamp());
    this.recordTimeStamp = bill.getDeliveryTimestamp();
    Short type;
    switch (bill.getOrderType()) {
      case 0://销售
        type = StockGoodsRecordEnums.Type.SALE_ORDER.getStatus();
        break;
      case 3://采购退货
        type = StockGoodsRecordEnums.Type.PURCHASE_RETURN_ORDER.getStatus();
        break;
      default:
        throw new BusinessException("订单类型异常！");
    }
    this.type = type;
    this.dataType = StockGoodsRecordEnums.DataType.ROLL_BACK.getStatus();
    this.storehouseId = bill.getDepotId();
    this.originalOrderId = bill.getOrderId();
    this.originalOrderCode = bill.getOrderCode();
    this.originalLogisticsId = bill.getId();
    this.originalLogisticsCode = bill.getCode();
    this.recordOrderCode = CodeGenerateUtil.codeGenerate(CodeExpEnum.STOCK_RECORD_BILL_EXP.getCodeExp(), this.storehouseId);
    this.inOutType = StockGoodsRecordEnums.InOutType.OUT.getStatus();

    this.items = new ArrayList<>();
    List<LogisticsBillItem> billItems = bill.getItems();
    billItems.stream().forEach(billItem -> {
      StockRecordItem stockRecordItem = new StockRecordItem(bill,billItem,StockGoodsRecordEnums.DataType.ROLL_BACK.getStatus());
      this.items.add(stockRecordItem);
    });
  }

  public void cancelReceipt(LogisticsBill bill) {
    this.businessEntityId = bill.getBusinessEntityId();
    this.checkSupplierOpenAccountState();
    this.checkArchiveState(bill.getStorageTimestamp());
    this.recordTimeStamp = bill.getStorageTimestamp();
    Short type;
    switch (bill.getOrderType()) {
      case 1://销售退货
        type = StockGoodsRecordEnums.Type.SALE_RETURN_ORDER.getStatus();
        break;
      case 2://采购
        type = StockGoodsRecordEnums.Type.PURCHASE_ORDER.getStatus();
        break;
      default:
        throw new BusinessException("订单类型异常！");
    }
    this.type = type;
    this.dataType = StockGoodsRecordEnums.DataType.ROLL_BACK.getStatus();
    this.storehouseId = bill.getDepotId();
    this.originalOrderId = bill.getOrderId();
    this.originalOrderCode = bill.getOrderCode();
    this.originalLogisticsId = bill.getId();
    this.originalLogisticsCode = bill.getCode();
    this.recordOrderCode = CodeGenerateUtil.codeGenerate(CodeExpEnum.STOCK_RECORD_BILL_EXP.getCodeExp(), this.storehouseId);
    this.inOutType = StockGoodsRecordEnums.InOutType.IN.getStatus();

    List<StockRecordItem> items = new ArrayList<>();
    List<LogisticsBillItem> billItems = bill.getItems();
    billItems.stream().forEach(billItem -> {
      StockRecordItem stockRecordItem = new StockRecordItem(bill,billItem,StockGoodsRecordEnums.DataType.ROLL_BACK.getStatus());
      items.add(stockRecordItem);
    });
    this.items = items;
  }

  /**
   * 创建报损出入库单
   *
   * @param loss
   */
  public void saveStockLossRecord(StockLoss loss) {
    this.businessEntityId = loss.getBusinessEntityId();
    this.checkSupplierOpenAccountState();
    this.checkArchiveState(loss.getInputDateTimestamp());
    this.recordTimeStamp = loss.getInputDateTimestamp();
    this.type = StockGoodsRecordEnums.Type.STOCK_LOSS_BILL.getStatus();
    this.storehouseId = loss.getStorehouseId();
    this.originalOrderId = loss.getId();
    this.originalOrderCode = loss.getLossOrderCode();
    this.operatorId = loss.getOperatorId();
    this.recordOrderCode = CodeGenerateUtil.codeGenerate(CodeExpEnum.STOCK_RECORD_BILL_EXP.getCodeExp(), this.storehouseId);
    this.inOutType = StockGoodsRecordEnums.InOutType.OUT.getStatus();
    switch (loss.getStatus()) {
      case 1:
        this.dataType = StockGoodsRecordEnums.DataType.NORMAL.getStatus();
        break;
      case 0:
        this.dataType = StockGoodsRecordEnums.DataType.ROLL_BACK.getStatus();
        break;
    }
    this.items = new ArrayList<>();
    List<StockLossItem> lossItems = loss.getLossItems();
    lossItems.stream().forEach(lossItem -> {
      StockRecordItem stockRecordItem = new StockRecordItem(lossItem, this.dataType);
      this.items.add(stockRecordItem);
    });
  }

  /**
   * 创建报溢出入库单
   *
   * @param spill
   */
  public void saveStockSpillRecord(StockSpill spill) {
    this.businessEntityId = spill.getBusinessEntityId();
    this.checkSupplierOpenAccountState();
    this.checkArchiveState(spill.getInputDateTimestamp());
    this.recordTimeStamp = spill.getInputDateTimestamp();
    this.type = StockGoodsRecordEnums.Type.STOCK_SPILL_BILL.getStatus();
    this.storehouseId = spill.getStorehouseId();
    this.originalOrderId = spill.getId();
    this.originalOrderCode = spill.getSpillCode();
    this.operatorId = spill.getOperatorId();
    this.recordOrderCode = CodeGenerateUtil.codeGenerate(CodeExpEnum.STOCK_RECORD_BILL_EXP.getCodeExp(), this.storehouseId);
    this.inOutType = StockGoodsRecordEnums.InOutType.IN.getStatus();

    switch (spill.getStatus()) {
      case 1:
        this.dataType = StockGoodsRecordEnums.DataType.NORMAL.getStatus();
        break;
      case 0:
        this.dataType = StockGoodsRecordEnums.DataType.ROLL_BACK.getStatus();
        break;
    }
    this.items = new ArrayList<>();
    List<StockSpillItem> spillItems = spill.getSpillItems();
    spillItems.stream().forEach(spillItem -> {
      StockRecordItem stockRecordItem = new StockRecordItem(spillItem, this.dataType);
      this.items.add(stockRecordItem);
    });
  }


  /**
   * 创建其他入库单
   *
   * @param stockOtherInBill
   */
  public void saveStockOtherInBill(StockOtherInBill stockOtherInBill,Short dataType) {
    this.businessEntityId = stockOtherInBill.getBusinessEntityId();
    this.checkSupplierOpenAccountState();
    this.checkArchiveState(stockOtherInBill.getRecordDateTimestamp());
    this.recordTimeStamp = stockOtherInBill.getRecordDateTimestamp();
    this.type = StockGoodsRecordEnums.Type.STOCK_OTHER_IN_BILL.getStatus();
    this.storehouseId = stockOtherInBill.getStorehouseId();
    this.originalOrderId = stockOtherInBill.getId();
    this.originalOrderCode = stockOtherInBill.getCode();
    this.operatorId = stockOtherInBill.getOperatorId();
    this.recordOrderCode = CodeGenerateUtil.codeGenerate(CodeExpEnum.STOCK_RECORD_BILL_EXP.getCodeExp(), this.storehouseId);
    this.inOutType = StockGoodsRecordEnums.InOutType.IN.getStatus();

    switch (dataType) {
      case 1:
        this.dataType = StockGoodsRecordEnums.DataType.NORMAL.getStatus();
        break;
      case 2:
        this.dataType = StockGoodsRecordEnums.DataType.ROLL_BACK.getStatus();
        break;
    }
    this.items = new ArrayList<>();
    List<StockOtherInBillItem> stockOtherInBillItems = stockOtherInBill.getItems();
    stockOtherInBillItems.stream().forEach(stockOtherInBillItem -> {
      StockRecordItem stockRecordItem = new StockRecordItem(stockOtherInBillItem, this.dataType);
      this.items.add(stockRecordItem);
    });
  }

  /**
   * 创建其他出库单
   *
   * @param stockOtherOutBill
   */
  public void saveStockOtherOutBill(StockOtherOutBill stockOtherOutBill,Short dataType) {
    this.businessEntityId = stockOtherOutBill.getBusinessEntityId();
    this.checkSupplierOpenAccountState();
    this.checkArchiveState(stockOtherOutBill.getRecordDateTimestamp());
    this.recordTimeStamp = stockOtherOutBill.getRecordDateTimestamp();
    this.type = StockGoodsRecordEnums.Type.STOCK_OTHER_OUT_BILL.getStatus();
    this.storehouseId = stockOtherOutBill.getStorehouseId();
    this.originalOrderId = stockOtherOutBill.getId();
    this.originalOrderCode = stockOtherOutBill.getCode();
    this.operatorId = stockOtherOutBill.getOperatorId();
    this.recordOrderCode = CodeGenerateUtil.codeGenerate(CodeExpEnum.STOCK_RECORD_BILL_EXP.getCodeExp(), this.storehouseId);
    this.inOutType = StockGoodsRecordEnums.InOutType.OUT.getStatus();

    switch (dataType) {
      case 1:
        this.dataType = StockGoodsRecordEnums.DataType.NORMAL.getStatus();
        break;
      case 2:
        this.dataType = StockGoodsRecordEnums.DataType.ROLL_BACK.getStatus();
        break;
    }
    this.items = new ArrayList<>();
    List<StockOtherOutBillItem> stockOtherOutBillItems = stockOtherOutBill.getItems();
    stockOtherOutBillItems.stream().forEach(stockOtherOutBillItem -> {
      StockRecordItem stockRecordItem = new StockRecordItem(stockOtherOutBillItem,stockOtherOutBill.getStorehouseId(), this.dataType);
      this.items.add(stockRecordItem);
    });
  }

  /**
   * 检查服务商是否开账
   */
  private void checkSupplierOpenAccountState() {
    OperaSupplierReadRepository operaSupplierReadRepository = (OperaSupplierReadRepository) SpringContextUtils.getBean("operaSupplierRepositoryImpl");
    OperaSupplier operaSupplier = operaSupplierReadRepository.getById(this.businessEntityId);
    if (!Short.valueOf("1").equals(operaSupplier.getOpenAccountState())) {
      throw new BusinessException("当前服务商未开账，生成出入库记录失败！");
    }
  }

  /**
   * 根据提供的时间戳判断是否在结存范围内
   * @param timeStamp
   * @return
   */
  private void checkArchiveState(Long timeStamp) {
    ArchiveReadRepository archiveReadRepository = (ArchiveReadRepository) SpringContextUtils.getBean("archiveRepoImpl");
    Wrapper wrapper = Wrapper.build().append(Restrictions.gt("archiveEndTimeStamp",timeStamp));
    if (archiveReadRepository.getCount(wrapper) > 0) {
      SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
      throw new BusinessException("该库存记录时间"+sdf.format(new Date(timeStamp))+"已结存，创建失败！");
    }
  }

  /**
   * 出入库单号
   */
  private String recordOrderCode;
  /**
   * 对应单号id
   */
  private Long originalOrderId;
  /**
   * 对应单号
   */
  private String originalOrderCode;
  /**
   * 对应物流单号
   */
  private String originalLogisticsCode;
  /**
   * 对应物流单id
   */
  private Long originalLogisticsId;
  /**
   * 出入库类型(1：销售出库 2：销售退货入库 3：采购入库 4：采购退货出库 5：报损出库 6：报溢入库 7：其他入库 8：其他出库)
   */
  private Short type;
  /**
   * 出入库类型(1：出库、2：入库)
   */
  private Short inOutType;
  /**
   * 数据类型: 1正规操作, 2回滚操作
   */
  private Short dataType;
  /**
   * 仓库id
   */
  private Long storehouseId;
  /**
   * 操作人id
   */
  private Long operatorId;
  /**
   * 归属id
   */
  private Long businessEntityId;
  /**
   * 业务记录日期
   */
  private Long recordTimeStamp;
  /**
   * 出入库子项
   */
  private List<StockRecordItem> items;
}
